package io.noties.markwon.base.core.spans

import android.text.style.MetricAffectingSpan
import android.text.TextPaint
import android.text.style.LeadingMarginSpan
import io.noties.markwon.base.utils.LeadingMarginUtils
import android.graphics.*
import android.text.Layout
import androidx.annotation.IntRange
import com.gitee.wsl.ext.base.setAlpha
import io.noties.markwon.theme.MarkwonTheme
import java.lang.IllegalStateException
import java.util.*

/**
 * @since 4.2.0
 */
open class HeadingSpan(
    private val theme: MarkwonTheme,
    @param:IntRange(from = 1, to = 6) val level: Int
) : MetricAffectingSpan(), LeadingMarginSpan {
    private val rect = ObjectsPool.rect()
    private val paint = ObjectsPool.paint()

    override fun updateMeasureState(p: TextPaint) {
        apply(p)
    }

    override fun updateDrawState(tp: TextPaint) {
        apply(tp)
    }

    private fun apply(paint: TextPaint) {
        applyHeadingTextStyle(paint, level)
    }

    private fun applyHeadingTextStyle(paint: TextPaint, level: Int) {
        if (theme.typography.headingTypeface.isEmpty()) {
            paint.isFakeBoldText = true
        } else {
            paint.typeface = Typeface.create(
                theme.typography.headingTypeface,
                Typeface.BOLD
            )
        }
        val textSizes = theme.typography.headingTextSizeMultipliers
        if (textSizes.size >= level) {
            paint.textSize = paint.textSize * textSizes[level - 1]
        } else {
            throw IllegalStateException(
                String.format(
                    Locale.US,
                    "Supplied heading level: %d is invalid, where configured heading sizes are: `%s`",
                    level, textSizes.contentToString()
                )
            )
        }

        paint.color=theme.colorTheme.headingColor.toInt()

    }

    override fun getLeadingMargin(first: Boolean): Int {
        // no margin actually, but we need to access Canvas to draw break
        return 0
    }

    override fun drawLeadingMargin(
        c: Canvas,
        p: Paint,
        x: Int,
        dir: Int,
        top: Int,
        baseline: Int,
        bottom: Int,
        text: CharSequence,
        start: Int,
        end: Int,
        first: Boolean,
        layout: Layout
    ) {
        if ((level == 1 || level == 2)
            && LeadingMarginUtils.selfEnd(end, text, this)
        ) {
            paint.set(p)
            applyHeadingBreakStyle(paint)
            val height = paint.strokeWidth
            if (height > .0f) {
                val b = (bottom - height + .5f).toInt()
                val left: Int
                val right: Int
                if (dir > 0) {
                    left = x
                    right = c.width
                } else {
                    left = x - c.width
                    right = x
                }
                rect[left, b, right] = bottom
                c.drawRect(rect, paint)
            }
        }
    }

    private fun applyHeadingBreakStyle(paint: Paint) {
        val color: Int = if (theme.colorTheme.headingBreakColor != 0L) {
            theme.colorTheme.headingBreakColor.toInt()
        } else {
           paint.color.setAlpha(HEADING_DEF_BREAK_COLOR_ALPHA)
        }
        paint.color = color
        paint.style = Paint.Style.FILL
        if (theme.typography.headingBreakHeight >= 0) {
            paint.strokeWidth = theme.typography.headingBreakHeight.toFloat()
        }
    }

    companion object {
        protected const val HEADING_DEF_BREAK_COLOR_ALPHA = 75
    }
}